home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / archiver / pdtar.zip / EXTRACT.C < prev    next >
C/C++ Source or Header  |  1988-05-15  |  11KB  |  389 lines

  1. /*
  2.  * Extract files from a tar archive.
  3.  *
  4.  * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  5.  * MS-DOS port 2/87 by Eric Roskos.
  6.  * Minix  port 3/88 by Eric Roskos.
  7.  *
  8.  * @(#) extract.c 1.17 86/10/29 Public Domain - gnu
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <errno.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15.  
  16. #ifdef BSD42
  17. #include <sys/file.h>
  18. #endif
  19.  
  20. #ifdef USG
  21. #include <fcntl.h>
  22. #endif
  23.  
  24. extern int      errno;            /* From libc.a */
  25. extern char    *index();        /* From libc.a or port.c */
  26.  
  27. #include "tar.h"
  28.  
  29. extern union record *head;        /* Points to current tape header */
  30. extern struct stat hstat[1];    /* Stat struct corresponding */
  31. extern struct stat *phstat;    /* overcome original highly arcane C syntax */ 
  32.  
  33. extern void     print_header();
  34. extern void     skip_file();
  35. extern void     pr_mkdir();
  36.  
  37. int             make_dirs();    /* Makes required directories */
  38.  
  39. time_t          now = 0;        /* Current time */
  40.  
  41. /*
  42.  * Extract a file from the archive.
  43.  * Note: xname is the "fixed name" which is supposed to be acceptable
  44.  * to the OS.  We use this instead of the name in the header, but only
  45.  * if we're actually creating a data file.
  46.  */
  47. void
  48. extract_archive(xname)
  49. char           *xname;
  50. {
  51.     register char  *data;
  52.     int             fd, check, namelen, written;
  53.     long            size;
  54.     time_t          acc_upd_times[2];
  55.     int             standard;    /* Is header standard? */
  56.     struct stat     st;
  57.  
  58.     saverec(&head);                /* Make sure it sticks around */
  59.     userec(head);                /* And go past it in the archive */
  60.     decode_header(head, hstat, &standard, 1);    /* Snarf fields */
  61.  
  62.     /* Print the record from 'head' and 'hstat' */
  63.     if (f_verbose)
  64.         print_header(xname);
  65.  
  66.     switch (head->header.linkflag)
  67.     {
  68.  
  69.     default:
  70.         annofile(stderr, tar);
  71.         fprintf(stderr, "Unknown file type %d for %s\n",
  72.             head->header.linkflag, head->header.name);
  73.         /* FALL THRU */
  74.  
  75.     case LF_OLDNORMAL:
  76.     case LF_NORMAL:
  77.  
  78.         /*
  79.          * Appears to be a file. See if it's really a directory. 
  80.          */
  81.         namelen = strlen(head->header.name) - 1;
  82.         if (head->header.name[namelen] == '/')
  83.             goto really_dir;
  84.  
  85.         /* FIXME, deal with protection issues */
  86.         /* FIXME, f_keep doesn't work on V7, st_mode loses too */
  87. again_file:
  88. #ifdef V7
  89.         fd = creat(xname, phstat->st_mode);
  90. #else        
  91.         fd = open( /* head->header.name */ xname,
  92.             f_keep ?
  93.             O_NDELAY | O_WRONLY | O_APPEND | O_CREAT |
  94.             O_EXCL | convmode(xname) :
  95.             O_NDELAY | O_WRONLY | O_APPEND | O_CREAT | O_TRUNC |
  96.             convmode(xname),
  97.             phstat->st_mode);
  98. #endif
  99.         if (fd < 0)
  100.         {
  101.             if (make_dirs(head->header.name,    /* use original name here */
  102.                 from_oct(8, head->header.uid),
  103.                 from_oct(8, head->header.gid))) /* added uid/gid - JER */
  104.                 goto again_file;
  105.             annofile(stderr, tar);
  106.             fprintf(stderr, "Could not make file ");
  107.             perror( /* head->header.name */ xname);
  108.             skip_file((long) phstat->st_size);
  109.             goto quit;
  110.         }
  111.  
  112.         for (size = phstat->st_size;
  113.             size > 0;
  114.             size -= written)
  115.         {
  116.  
  117.             /*
  118.              * Locate data, determine max length writeable, write it, record
  119.              * that we have used the data, then check if the write worked. 
  120.              */
  121.             data = findrec()->charptr;
  122.             written = endofrecs()->charptr - data;
  123.             if (written > size)
  124.                 written = size;
  125.             errno = 0;
  126.             check = write(fd, data, written);
  127.  
  128.             /*
  129.              * The following is in violation of strict typing, since the arg
  130.              * to userec should be a struct rec *. FIXME. 
  131.              */
  132.             userec(data + written - 1);
  133.             if (check == written)
  134.                 continue;
  135.  
  136.             /*
  137.              * Error in writing to file. Print it, skip to next file in
  138.              * archive. 
  139.              */
  140.             annofile(stderr, tar);
  141.             fprintf(stderr,
  142.                 "Tried to write %d bytes to file, could only write %d:\n",
  143.                 written, check);
  144.             perror( /* head->header.name */ xname);
  145.             (void) close(fd);
  146.             skip_file((long) (size - written));
  147.             goto quit;
  148.         }
  149.  
  150.         check = close(fd);
  151.         if (check < 0)
  152.         {
  153.             annofile(stderr, tar);
  154.             fprintf(stderr, "Error while closing ");
  155.             perror( /* head->header.name */ xname);
  156.         }
  157.  
  158. #ifndef MSDOS
  159.         /* deal with uid/gid FIXME: mtimes/suid */
  160.         /* FIXME - should use new name-string fields if present */
  161.         chown(xname, from_oct(8, head->header.uid),
  162.             from_oct(8, head->header.gid)); /* JER */
  163. #endif
  164.         /*
  165.          * Set the modified time of the file. 
  166.          *
  167.          * Note that we set the accessed time to "now", which is really "the
  168.          * time we started extracting files". 
  169.          */
  170.         if (!f_modified)
  171.         {
  172.             if (!now)
  173.                 now = time((time_t *) 0);        /* Just do it once */
  174.             acc_upd_times[0] = now;        /* Accessed now */
  175.             acc_upd_times[1] = phstat->st_mtime;    /* Mod'd */
  176.             if (utime( /* head->header.name */ xname, acc_upd_times) < 0)
  177.             {
  178.                 annofile(stderr, tar);
  179.                 perror( /* head->header.name */ xname);
  180.             }
  181.         }
  182.  
  183.         /*
  184.          * If '-p' is not set, OR if the file has pretty normal mode bits, we
  185.          * can skip the chmod and save a sys call. This works because we did
  186.          * umask(0) if -p is set, so the open() that created the file will
  187.          * have set the modes properly. FIXME: I don't know what open() does
  188.          * w/UID/GID/SVTX bits. However, if we've done a chown(), they got
  189.          * reset. Also skip CHMOD for MS/DOS (at least for now) since none of
  190.          * these bits are defined. 
  191.          */
  192. #ifndef MSDOS
  193.         if (f_use_protection
  194.             && (phstat->st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
  195.         {
  196.             if (chmod( /* head->header.name */ xname, (int) phstat->st_mode) < 0)
  197.             {
  198.                 annofile(stderr, tar);
  199.                 perror( /* head->header.name */ xname);
  200.             }
  201.         }
  202. #endif
  203.  
  204. quit:
  205.         break;
  206.  
  207.     case LF_LINK:
  208. again_link:
  209. #ifndef MSDOS
  210.         check = link(head->header.linkname,
  211.             head->header.name);
  212.         if (check == 0)
  213.             break;
  214.         if (make_dirs(head->header.linkname,
  215.             from_oct(8, head->header.uid),
  216.             from_oct(8, head->header.gid))) /* added uid/gid - JER */
  217.             goto again_link;
  218.         annofile(stderr, tar);
  219.         fprintf(stderr, "Could not link %s to ",
  220.             head->header.name);
  221.         perror(head->header.linkname);
  222. #else
  223.         fprintf(stderr, "Cannot link %s to %s: linking not supported by DOS\n",
  224.             head->header.name, head->header.linkname);
  225. #endif
  226.         break;
  227.  
  228. #ifdef S_IFLNK
  229.     case LF_SYMLINK:
  230. again_symlink:
  231.         check = symlink(head->header.linkname,
  232.             head->header.name);
  233.         /* FIXME, don't worry uid, gid, etc... */
  234.         if (check == 0)
  235.             break;
  236.         if (make_dirs(head->header.linkname,
  237.             from_oct(8, head->header.uid),
  238.             from_oct(8, head->header.gid))) /* added uid/gid - JER */
  239.             goto again_symlink;
  240.         annofile(stderr, tar);
  241.         fprintf(stderr, "Could not create symlink ");
  242.         perror(head->header.linkname);
  243.         break;
  244. #endif
  245.  
  246.     case LF_CHR:
  247.         phstat->st_mode |= S_IFCHR;
  248.         goto make_node;
  249.  
  250. #ifdef S_IFBLK
  251.     case LF_BLK:
  252.         phstat->st_mode |= S_IFBLK;
  253. #endif
  254. make_node:
  255. #ifndef MSDOS
  256.         /* FIXME: constant 1024 should be #define'd fs blocksize.  But,
  257.          * this only works on my Minix system anyway; on a standard
  258.          * Minix or Unix, the 3rd parameter will be ignored. */
  259.         check = mknod(head->header.name, (int) phstat->st_mode,
  260.             (int) phstat->st_dev, (int)(phstat->st_size/1024));
  261.         if (check != 0)
  262.         {
  263.             if (make_dirs(head->header.name,
  264.                 from_oct(8, head->header.uid),
  265.                 from_oct(8, head->header.gid))) /* added uid/gid - JER */
  266.                 goto make_node;
  267.             annofile(stderr, tar);
  268.             fprintf(stderr, "Could not make special file ");
  269.             perror(head->header.name);
  270.             break;
  271.         };
  272. #else
  273.         /* with DOS, either it's there or you've got to change driver */
  274.         annofile(stderr, tar);
  275.         fprintf(stderr, "Cannot create special file %s\n",
  276.             head->header.name);
  277. #endif
  278.         break;
  279.  
  280.     case LF_DIR:
  281.         /* Check for trailing / */
  282.         namelen = strlen(head->header.name) - 1;
  283. really_dir:
  284.         while (namelen && head->header.name[namelen] == '/')
  285.             head->header.name[namelen--] = '\0';        /* Zap / */
  286.  
  287.         /* FIXME, deal with umask */
  288. again_dir:
  289. #ifdef MSDOS
  290.  
  291.         /*
  292.          * don't do the mkdir under MSDOS if the dir already exists. (Won't
  293.          * this give an error under Unix too?  Should it? I don't think
  294.          * Unix's tar does.) 
  295.          */
  296.         if (stat(head->header.name, &st) == 0 && (st.st_mode & S_IFDIR))
  297.             check = 0;
  298.         else
  299. #endif
  300.             check = m